#include <iostream>
#include "rapidjson/serialize.hpp"
#include "rapidjson/deserialize.hpp"
#include "rapidjson/meta.h"

struct Test {
    int a;
    short b;
    char c;
    std::string d;
    std::bitset<8> e;
    std::vector<int> f;
    std::list<std::string> g;

    bool operator<(const Test &tt) const {return a < tt.a;} //for std::set

    void print() const
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
        std::cout << "c = " << (int)c << std::endl;
        std::cout << "d = " << d << std::endl;
        std::cout << "e = " << e << std::endl;
        std::cout << "f = [";
        for (auto &v : f)
            std::cout << v << ' ';
        std::cout << ']' << std::endl;
        std::cout << "g = [";
        for (auto &v : g)
            std::cout << v << ' ';
        std::cout << ']' << std::endl << std::endl;
    }

    METAS(a, b, c, d, e, f, g);
};

Test t1{100, 200, 3, "hello", 30, {1, 2, 3, 4}, {"a", "b", "c"}};
Test t2{1, 2, 3, "yes", 31, {5, 6, 7, 8}, {"d", "e", "f"}};
Test t3{4, 5, 6, "no", 32, {9, 10, 11, 12}, {"g", "h", "i"}};

template<typename T>
void test_container()
{
    T arr{t1, t2, t3};
    std::cout << rapidjson::serialize(arr) << std::endl;
}

void test_mix()
{
    test_container<std::vector<Test>>();
    test_container<std::list<Test>>();
    test_container<std::set<Test>>();
    test_container<std::array<Test, 3>>();
    
    std::map<int, Test> mp;
    mp[1] = t1;
    mp[2] = t2;
    mp[3] = t3;
    std::string str = rapidjson::serialize(mp);
    std::cout << str << std::endl;

    std::map<int, Test> rmp;
    std::string err;
    if (rapidjson::deserialize(rmp, str, err)) {
        for (auto &it : rmp) {
            std::cout << "key = " << it.first << std::endl;
            it.second.print();
        }
    } else {
        std::cout << err << std::endl;
    }
}

struct Object {
    int a;
    short b;
    METAS(a, b);
};

struct Carray {
    Object objs[2];
    uint8_t numbers[2];
    METAS(objs, numbers);
};

void test_carray()
{
    Carray carr;
    carr.objs[0] = {100, 10};
    carr.objs[1] = {200, 20};
    carr.numbers[0] = 111;
    carr.numbers[1] = 222;
    auto &&str = rapidjson::serialize(carr);
    std::cout << str << std::endl;

    Carray tmp;
    memset(&tmp, 0, sizeof(tmp));
    if (rapidjson::deserialize(tmp, str)) {
        std::cout << tmp.objs[0].a << " " << tmp.objs[0].b << std::endl;
        std::cout << tmp.objs[1].a << " " << tmp.objs[1].b << std::endl;
        std::cout << (int)tmp.numbers[0] << std::endl;
        std::cout << (int)tmp.numbers[1] << std::endl;
    } else {
        std::cout << "deserialize fail" << std::endl;
    }
}

void test_map_string_string()
{
    std::unordered_map<std::string, std::string> ms, dms;
    ms["Jicky"] = "123";
    ms["Nicky"] = "456";
    std::string json = rapidjson::serialize(ms);
    std::cout << json << std::endl;
    std::string err;
    if (rapidjson::deserialize(dms, json, err)) {
        for (auto &it : dms) {
            std::cout << "key:" << it.first << ", value:" << it.second << std::endl;
        }
    } else {
        std::cout << err << std::endl;
    }
}

void test_map_int_int()
{
    std::unordered_map<int, int> ms, dms;
    ms[1] = 123;
    ms[2] = 456;
    std::string json = rapidjson::serialize(ms);
    std::cout << json << std::endl;
    std::string err;
    if (rapidjson::deserialize(dms, json, err)) {
        for (auto &it : dms) {
            std::cout << "key:" << it.first << ", value:" << it.second << std::endl;
        }
    } else {
        std::cout << err << std::endl;
    }
}

void test_map_int_map()
{
    std::unordered_map<int, std::map<int,int>> ms, dms;
    ms[1][11] = 123;
    ms[1][12] = 234;
    ms[2][22] = 456;
    std::string json = rapidjson::serialize(ms);
    std::cout << json << std::endl;
    std::string err;
    if (rapidjson::deserialize(dms, json, err)) {
        for (auto &it : dms) {
            std::cout << "key:" << it.first << ", value:" << std::endl;
            for (auto &iit : it.second) {
                std::cout << "\tkey:" << iit.first << ", value:" << iit.second << std::endl;
            }
        }
    } else {
        std::cout << err << std::endl;
    }
}

int main(void)
{
    test_map_string_string();
    test_map_int_int();
    test_map_int_map();
    return 0;
}